// ========== 改进 ==========
// v1. 继承与多态实现所有功能 √
// v2. 增加map<string, int> 与 map<string, set<int>>
// v2.1 检查构造函数的流程
// v3. 将TextQuery改为单例
// v4. 改为bind+function ×感觉目前调用不是很优雅

// ========== 头文件 ==========
#include <iostream>
#include <fstream>
#include <sstream>

#include <vector>
#include <set>
#include <map>

#include <memory>
#include <functional>

using namespace std;

// ========== 宏函数 ==========
#define ERROR_CHECK(cond, msg, cleanup) \
    do{ \
        if(cond) { \
            std::cerr << "Error:" << msg << "(" << __FILE__ << ":" << __LINE__ << ")" << std::endl; \
            cleanup; \
            return; \
        } \
    }while(0)
// ========== 前向声明 ==========
class TextQuery;    // 将文本文件的内容保存在vector<string>中，返回QueryResult对象
class QueryResult;  // 表示查询结果，通常与print联合使用
//class Query_base;   // eval相当于TextQuery的query函数，参数是TextQuery，返回QueryResult；rep函数用于返回查询的string表示形式
//class Query;        // 用于获得shared_ptr，并且重载运算符
//class WordQuery;
//class NotQuery;

// ========== 具体实现1 ==========
class TextQuery
{
private:
    shared_ptr<vector<string>> _text_ptr;   // 存储所有文本内容
    map<string, shared_ptr<set<int>>> _word_map;
    map<string, int> _freq_map;

public:
    TextQuery(const string&);               // 传文件名
    QueryResult query(const string&) const; // 传查询单词
    shared_ptr<vector<string>> get_text_data() const;    

};

class QueryResult
{
public:
    enum class QueryType {WORD, AND, OR, NOT};

    //friend class TextQuery;
private:
    //shared_ptr<vector<string>> _text_vec_ptr;   // 共享指针内容
    //shared_ptr<map<string, set<int>>> _word_map;
    //shared_ptr<map<string, int>> _freq_map;
    
    //weak_ptr<const vector<string>> _text;       // 观察文本数据（不需要所有权）,但也无法取出数据，除非强转为shared_ptr
    QueryType _type;
    shared_ptr<const vector<string>> _text;
    shared_ptr<set<int>> _result_lines;                     // 独立的结果行号
    int _result_freq;                           // 词频统计
    string _query_word;                         // 唯一的作用：打印的时候显示

public:

    //QueryResult();  // 去掉默认函数，避免无参数查询
    QueryResult(QueryType,
                shared_ptr<const vector<string>>, 
                shared_ptr<set<int>>, 
                int, 
                string);      // 需要string，否则print搞不清楚参数    
    
    //void printresult(const string&) const;
    void printresult() const;
    shared_ptr<const vector<string>> get_text() const;
    shared_ptr<set<int>> get_result_lines() const;
    string get_query_word() const;

};

class Query_base
{
public:
    //    // 纯虚函数接口
    virtual QueryResult eval(const TextQuery&) const = 0;
    virtual ~Query_base() = default;    // 基类必须有虚析构函数

    virtual string rep() const = 0;     // 不接受参数，由子类自行维护状态

};

// 1: 暂时考虑仅写Base, 用bind写，一步跳过调用指针
// 2: 发现跳不过去，运算符的重载还指望Query实现
// 3: Query本质是多态代理类，多态的控制中心
class Query
{
private:
    // 1: 不能直接实例化抽象类，仅能通过指针引用
    // 2: 通过指针实现多态
    shared_ptr<Query_base> _query_base_ptr;     

public:
    Query(shared_ptr<Query_base>);
    Query(const string&);
    shared_ptr<Query_base> get_query() const;   // 获取指针

    QueryResult eval(const TextQuery&) const;
    string rep() const;

    friend Query operator~(const Query&);
    friend Query operator&(const Query&, const Query&);
    friend Query operator|(const Query&, const Query&);
};

class WordQuery : public Query_base
{
private:
    string _word;
public:
    WordQuery(const string& word);

    QueryResult eval(const TextQuery&) const override;
    string rep() const override;
};

class NotQuery : public Query_base
{
private:
    shared_ptr<Query_base> _query;
public:
    // 写shared_ptr<Query_Base> 与 const Query& 都可以委托WordQuery
    // operator~ 会走Query -> make_shared<NotQuery> 创建NotQuery对象 -> 基类指针指向QUery -> 走WordQuery
    //not_q._query 
    //      → 指向 NotQuery对象 
    //           → NotQuery::_query 
    //                   → 指向原始 WordQuery对象
    NotQuery(shared_ptr<Query_base> query);

    QueryResult eval(const TextQuery&) const override;
    string rep() const override;
};

class BinaryQuery : public Query_base
{
protected:
    shared_ptr <Query_base> lhs, rhs;
    string op_symbol;

    BinaryQuery(shared_ptr<Query_base>, shared_ptr<Query_base>, string);
    string rep() const override;    // 留着还有点用处
};

class AndQuery : public BinaryQuery
{
public:
    AndQuery(shared_ptr<Query_base>, shared_ptr<Query_base>);

    QueryResult eval(const TextQuery&) const override;

};

class OrQuery : public BinaryQuery
{
public:
    OrQuery(shared_ptr<Query_base>, shared_ptr<Query_base>);

    QueryResult eval(const TextQuery&) const override;

};

// ========== 具体实现2 ==========
TextQuery::TextQuery(const string &filename)
    :_text_ptr(make_shared<vector<string>>())
    //,_word_map(map<string, make_shared<set<int>>>())  // 语法有问题
    ,_word_map()
    ,_freq_map(map<string, int>())
    {
        ifstream ifs(filename);
        ERROR_CHECK(!ifs.good(), "ifs is not good", ifs.close());

        string line;
        size_t line_index = 0;
        while(getline(ifs, line)){
            _text_ptr->push_back(line);

            istringstream iss(line);
            string word;

            ++line_index;
            while(iss >> word){
                if(_word_map.find(word) == _word_map.end()){
                    _word_map[word] = make_shared<set<int>>();
                }

                _freq_map[word]++;
                _word_map[word]->insert(line_index);
                //(*_freq_map)[word]++;   // 智能指针，需要解引用后使用
                //(*_word_map)[word].insert(line_index);
            }
        }

        ifs.close();
    }

QueryResult TextQuery::query(const string& word) const
{
    // 在TextQuery中已有查询，所以此处不需要再重复查询，返回值即可
    //auto _query_word_map = make_shared<map<string, set<int>>>;
    //auto _query_freq_map = make_shared<map<string, int>>;

    return {
        QueryResult::QueryType::WORD,   // 类型默认为word
        _text_ptr,              // 共享智能指针转为weak_ptr观察
        _word_map.at(word),     // 结果集副本
        _freq_map.at(word),     // 词频值拷贝
        word                    // 查询词
    };
}

shared_ptr<vector<string>> TextQuery::get_text_data() const
{
    return _text_ptr;
}

QueryResult::QueryResult(QueryType type,
                         shared_ptr<const vector<string>> text, 
                         shared_ptr<set<int>> result_lines, 
                         int result_freq, 
                         string query_word)
    :_type(type)
    ,_text(text) 
    ,_result_lines(result_lines)
    ,_result_freq(result_freq)
    ,_query_word(query_word){}

void QueryResult::printresult() const
{
    if(_type == QueryResult::QueryType::WORD)
    {
        cout << "查询: " << _query_word << endl;
        cout << "次数: " << _result_freq << endl;
        cout << "行号: ";
    }
    else
    {
        cout << "单词: " << _query_word << endl;
        cout << "行数: " << _result_freq << endl;
        cout << "行号: ";
    }

    for(auto& line_num : *_result_lines){
        cout << line_num << " ";
    }
    
    cout << "\n";
        
    cout << "内容: " << "\n";
    for(auto& line_num : *_result_lines){
        cout << "第" << line_num << " 行: " << (*_text)[line_num - 1] << "\n";
    }
    cout << "\n";
}

shared_ptr<const vector<string>> QueryResult::get_text() const
{
    return _text;
}

shared_ptr<set<int>> QueryResult::get_result_lines() const
{
    return _result_lines;
}

string QueryResult::get_query_word() const
{
    return _query_word;
}

Query::Query(shared_ptr<Query_base> query_base_ptr)
    :_query_base_ptr(query_base_ptr){}

Query::Query(const string& word)
    //:_query_base_ptr(make_shared<Query_base>(word)){}     // 抽象类不允许实例化
    :_query_base_ptr(make_shared<WordQuery>(word)){}

shared_ptr<Query_base> Query::get_query() const
{
    return _query_base_ptr;
}

QueryResult Query::eval(const TextQuery& tq) const
{
    //return this->eval(word);
    return _query_base_ptr->eval(tq);     // 调用多态
}

string Query::rep() const
{
    return _query_base_ptr->rep();               // 返回字符串word
}

Query operator~(const Query& operand)
{
    //return make_shared<NotQuery>(operand._query_base_ptr);
    //return Query(make_shared<NotQuery>(operand.get_query()));

    // 1. operand._query_base_ptr → 取出原查询的基类指针
    // 2. 用该指针构造 NotQuery
    // 3. 将 NotQuery 包装为新 Query 对象
    // 4. 调用eval时，通过虚表找到NotQuery::eval
    return Query(make_shared<NotQuery>(operand._query_base_ptr));
}

Query operator&(const Query& lhs, const Query& rhs)
{
    return Query(make_shared<AndQuery>(lhs._query_base_ptr, rhs._query_base_ptr));
}

Query operator|(const Query& lhs, const Query& rhs)
{
    return Query(make_shared<OrQuery>(lhs._query_base_ptr, rhs._query_base_ptr));
}

WordQuery::WordQuery(const string& word)
    :_word(word){}

QueryResult WordQuery::eval(const TextQuery& tq) const
{
    //return this->eval(word);
    return tq.query(_word);     // 返回TextQuery类型的查询结果
}

string WordQuery::rep() const
{
    return _word;               // 返回字符串word
}

NotQuery::NotQuery(shared_ptr<Query_base> query)
    :_query(query){}

QueryResult NotQuery::eval(const TextQuery& tq) const
{   
    //1: 调用被取反查询的eval方法(多态调用)
    auto result = _query->eval(tq);  // 就当query会自己走TextQuery吧
    
    //2: 计算不再result中的所有行号
    shared_ptr<set<int>> not_lines = make_shared<set<int>>();
    int total_lines = result.get_text()->size();

    for(int i = 1; i <= total_lines; ++i)
    {
        if(result.get_result_lines()->find(i) == result.get_result_lines()->end())
        {
            not_lines->insert(i);
        }
    }

    return {
        QueryResult::QueryType::NOT,
        result.get_text(),
        not_lines,
        static_cast<int>(not_lines->size()),
        result.get_query_word()
    };
}

string NotQuery::rep() const
{
    return "~" + _query->rep();
}


BinaryQuery::BinaryQuery(shared_ptr<Query_base> left, shared_ptr<Query_base> right, string symbol)
     :lhs(move(left))
     ,rhs(move(right))
     ,op_symbol(move(symbol)){}
 
string BinaryQuery::rep() const
{
    return "(" + lhs->rep() + " " + rhs->rep() + ")";
}

AndQuery::AndQuery(shared_ptr<Query_base> left, shared_ptr<Query_base> right)
    : BinaryQuery(left, right, "&"){}

QueryResult AndQuery::eval(const TextQuery& tq) const{
    auto left_result = lhs->eval(tq);   // 返回结果是QueryResult
    auto right_result = rhs->eval(tq);

    auto result_set = make_shared<set<int>>();
    set_intersection(
        left_result.get_result_lines()->begin(),
        left_result.get_result_lines()->end(),
        right_result.get_result_lines()->begin(),
        right_result.get_result_lines()->end(),
        inserter(*result_set, result_set->begin())
    );

    return QueryResult{
        QueryResult::QueryType::AND,
        left_result.get_text(),
        result_set,
        static_cast<int>(result_set->size()),
        "(" + left_result.get_query_word() + " & " + right_result.get_query_word() + ")"   
    };   
}


OrQuery::OrQuery(shared_ptr<Query_base> left, shared_ptr<Query_base> right)
    : BinaryQuery(left, right, "&"){}

QueryResult OrQuery::eval(const TextQuery& tq) const{
    auto left_result = lhs->eval(tq);   // 返回结果是QueryResult
    auto right_result = rhs->eval(tq);

    auto result_set = make_shared<set<int>>();
    set_union(
        left_result.get_result_lines()->begin(),
        left_result.get_result_lines()->end(),
        right_result.get_result_lines()->begin(),
        right_result.get_result_lines()->end(),
        inserter(*result_set, result_set->begin())
    );

    return QueryResult{
        QueryResult::QueryType::AND,
        left_result.get_text(),
        result_set,
        static_cast<int>(result_set->size()),
        "(" + left_result.get_query_word() + " | " + right_result.get_query_word() + ")"   
    };   
}

// ========== 测试函数  ==========
void test_word(){
    TextQuery tq("17_china_daily.txt");
    WordQuery wq("and");
    wq.eval(tq).printresult();
}

void test_not(){
    TextQuery tq("17_china_daily.txt");
    //Query q = ~Query(make_shared<WordQuery>("and"));
    Query q = ~Query("and");
    cout << q.rep() << endl;
    q.eval(tq).printresult();
}

void test_and(){
    TextQuery tq("17_china_daily.txt");
    Query q1("we");
    Query q2("and");
    auto and_q = q1 & q2;
    
    cout << and_q.rep() << endl;
    and_q.eval(tq).printresult();
}

void test_or(){
    TextQuery tq("17_china_daily.txt");
    Query q1("is");
    Query q2("and");
    auto or_q = q1 | q2;
    
    cout << or_q.rep() << endl;
    or_q.eval(tq).printresult();
}

void test_all(){
    TextQuery tq("17_china_daily.txt");
    auto complex_q = (Query("is") | Query("and")) & ~Query("the");
    complex_q.eval(tq).printresult();
    std::cout << "查询表达式: " << complex_q.rep() << std::endl;
}

int main()
{
    test_word();
    test_not();
    test_and();
    test_or();
    test_all();
    return 0;
}

